package yagwl.client.model;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * Abstract class to manage cache of list of object on the client (browser)
 */
public abstract class ItemListCache {

  private static Logger log = Logger.getLogger(ItemListCache.class
      .getName());
  private boolean loadInProgress = false;
  private boolean fullyLoaded = false;
  private String historyToken;
  private int fetchSize = 100;
  private int sortOrderNumber;
  private List<ItemDTO> list = new ArrayList<ItemDTO>();

  /**
   * Gets a token to represent history on the URL.
   */
  public String getHistoryToken() {
    return historyToken;
  }

  /**
   * Sets a token to represent history on the URL.
   */
  public void setHistoryToken(String historyToken) {
    this.historyToken = historyToken;
  }

  protected int getSortOrder() {
    return sortOrderNumber;
  }

  /**
   * Sets the sort order number; every sort order is represented by a number.
   */
  public void setSortOrderNumber(int sortOrderNumber) {
    if (this.sortOrderNumber == sortOrderNumber)
      return;
    this.sortOrderNumber = sortOrderNumber;
    handleSortOrderChange();
  }

  protected abstract void handleSortOrderChange();

  protected void clearList() {
    list.clear();
    fullyLoaded = false;
  }

  public abstract void deleteObject(int objIdx);

  public abstract void updateObject(Object object);

  /**
   * Adds a newly created item directly in the cache instead of refreshing the
   * cache.
   */
  public void addItem(ItemDTO itemDTO) {
    list.add(itemDTO);
  }

  protected boolean isLoadInProgress() {
    return loadInProgress;
  }

  public boolean isFullyLoaded() {
    return fullyLoaded;
  }

  /**
   * Sets the fetch size of objects from server. Default is 100.
   * 
   * @param fetchSize
   */
  public void setFetchSize(int fetchSize) {
    this.fetchSize = fetchSize;
  }

  /**
   * Gets the object list.
   */
  public List<ItemDTO> getList() {
    return list;
  }

  /**
   * Fetches the next 'fetchSize' number of objects. The implementing method may
   * choose to fetch all objects.
   */
  public abstract void fetchNext();

  protected void loadStart() {
    loadInProgress = true;
    log.info("loadStart: Loading " + this.getClass().getName() + "("
        + getLoadStartIndex() + ", " + getLoadFetchSize() + ")");
  }

  protected int getLoadStartIndex() {
    return list.size();
  }

  protected int getLoadFetchSize() {
    return fetchSize + 1;
  }

  protected void onLoadSuccess(List<ItemDTO> result) {
    log.info("onLoadSuccess: Entering");
    loadInProgress = false;
    list.addAll(result);
    // print();
    if (result.size() < getLoadFetchSize()) {
      fullyLoaded = true;
    } else {
      list.remove(list.size() - 1); // because we loaded one extra
    }
    throwDataReadyEvent();
  }

  protected void onLoadFailure(Throwable caught) {
    loadInProgress = false;
    log.info(caught.toString());
  }

  /**
   * Throws ItemListEvent. Called when an asynchronous RPI for data manipulation
   * completes.
   */
  protected void throwDataReadyEvent() {
    DataReadyEventBus.getInstance().fireEvent(new ItemListEvent(this));
    log.info("throwDataReadyEvent: ItemListEvent raised: "
        + this.getClass().getName());
  }

  private void print() {
    log.info("print: " + this.getClass().getName());
    for (ItemDTO item : list) {
      log.info(item.getId() + ":" + item.getLabel());
    }
  }

}
